﻿using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Net;
using System.Net.Http;
using System.Net.Http.Headers;
using System.Threading.Tasks;
using System.Xml.Serialization;
using ContentAppBase.Model.Bing;
using Windows.ApplicationModel;
using Windows.Data.Json;
using Windows.Storage;
using Windows.UI.Xaml;

namespace ContentAppBase.Model
{
    public class DataService
    {
        private static DataService _current;
        public static DataService Current
        {
            get
            {
                if (_current != null)
                {
                    return _current;
                }

                _current = new DataService();
                return _current;
            }
        }

        #region Bing

        // 0 = Type (e.g. image or web)
        // 1 = Query (url encoded)
        // 2 = Market (e.g. de-DE)
        // 3 = Max Results (e.g. 50);
        private readonly String _bingBaseUrl = "https://api.datamarket.azure.com/Data.ashx/Bing/Search/v1/{0}?Query=%27{1}%27&Market=%27{2}%27&$top={3}&$format=json";
        private String _bingApiKey = (String)Application.Current.Resources["BingApiKey"];

        public async Task<List<ResultBase>> SearchBingAsync(String query, SearchTypes type, int count = 50, string locale = "de-DE")
        {
            try
            {
                var client = new HttpClient();
                client.DefaultRequestHeaders.Authorization = CreateBasicAuthorization(null, _bingApiKey);
                var jsonString = await client.GetStringAsync(String.Format(_bingBaseUrl, type, WebUtility.UrlEncode(query), locale, count));

                if (type == SearchTypes.Image)
                    return await Task.Run(() => ParseBingImageResults(jsonString));
                else if (type == SearchTypes.Web)
                    return await Task.Run(() => ParseBingWebResults(jsonString));
                else
                    return null;
            }
            catch (Exception ex)
            {
                return null;
            }
        }

        private List<ResultBase> ParseBingImageResults(string jsonString)
        {
            var jsonArray = ParseBingResults(jsonString);
            var result = from row in jsonArray
                         select new ImageResult()
                         {
                             Id = row.GetObject().GetNamedString("ID"),
                             Title = row.GetObject().GetNamedString("Title"),
                             MediaUrl = row.GetObject().GetNamedString("MediaUrl"),
                             SourceUrl = row.GetObject().GetNamedString("SourceUrl"),
                             DisplayUrl = row.GetObject().GetNamedString("DisplayUrl"),
                             Width = Double.Parse(row.GetObject().GetNamedString("Width")),
                             Height = Double.Parse(row.GetObject().GetNamedString("Height")),
                             FileSize = Double.Parse(row.GetObject().GetNamedString("FileSize")),
                             ContentType = row.GetObject().GetNamedString("ContentType"),
                             Thumbnail = new Thumbnail()
                             {
                                 MediaUrl = row.GetObject()["Thumbnail"].GetObject().GetNamedString("MediaUrl"),
                                 ContentType = row.GetObject()["Thumbnail"].GetObject().GetNamedString("ContentType"),
                                 Width = Double.Parse(row.GetObject()["Thumbnail"].GetObject().GetNamedString("Width")),
                                 Height = Double.Parse(row.GetObject()["Thumbnail"].GetObject().GetNamedString("Height")),
                                 FileSize = Double.Parse(row.GetObject()["Thumbnail"].GetObject().GetNamedString("FileSize"))
                             }
                         };

            return result.ToList<ResultBase>();
        }

        private List<ResultBase> ParseBingWebResults(string jsonString)
        {
            var jsonArray = ParseBingResults(jsonString);
            var result = from row in jsonArray
                         select new WebResult()
                         {
                             Id = row.GetObject().GetNamedString("ID"),
                             Title = row.GetObject().GetNamedString("Title"),
                             Description = row.GetObject().GetNamedString("Description"),
                             DisplayUrl = row.GetObject().GetNamedString("DisplayUrl"),
                             Url = row.GetObject().GetNamedString("Url")
                         };

            return result.ToList<ResultBase>();
        }

        private JsonArray ParseBingResults(string jsonString)
        {
            return JsonObject.Parse(jsonString).First().Value.GetObject()["results"].GetArray();
        }

        private AuthenticationHeaderValue CreateBasicAuthorization(string username, string password)
        {
            byte[] byteArray = System.Text.Encoding.UTF8.GetBytes(String.Format("{0}:{1}", username, password));
            return new AuthenticationHeaderValue("Basic", Convert.ToBase64String(byteArray));
        }

        #endregion

        #region Local

        private Dataset _currentDataset;

        public async Task<Dataset> GetDataset()
        {
            if (_currentDataset == null)
            {
                _currentDataset = await GetDataAsync<Dataset>("Content.xml");
            }

            return _currentDataset;
        }

        private async Task<T> GetDataAsync<T>(string fileName)
        {
            var raw = await GetXmlAsync(fileName);
            return await Task.Run(() => ParseData<T>(raw));
        }

        private T ParseData<T>(Stream raw)
        {
            XmlSerializer serializer = new XmlSerializer(typeof(T));
            return (T)serializer.Deserialize(raw);
        }

        private async Task<Stream> GetXmlAsync(string filename)
        {
            var storageFile = await Package.Current.InstalledLocation.GetFileAsync(@"Data\" + filename);
            var stream = await storageFile.OpenAsync(FileAccessMode.Read);
            return stream.AsStreamForRead();
        }

        #endregion
    }
}
